<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面，任何其他内容都*必须*跟随其后！ -->
    <meta name="description" content="">
    <meta name="author" content="">

    <title>QingFM App</title>

    <!-- Bootstrap core CSS -->
    <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <link href="/static/css/demo.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid">
    <div class="alert alert-info" role="alert">
        Tip:当您访问本网站时，我们的服务器会自动收集某些浏览器或设备生成的信息，包括但不限于：域名、IP地址、您访问的日期、时间和持续时间、浏览器类型、操作系统 和 页面访问情况。
    </div>

    <div class="page-header">
        <h1>音频解析 <small>Demo</small></h1>
    </div>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-4">
                <div class="panel panel-primary">
                    <div class="panel-heading">音乐列表</div>
                    <div class="panel-body">
                        <ul class="ul_regions">
                        </ul>
                    </div>
                </div>
            </div>
            <div class="col-md-8">
                <div class="panel panel-primary">
                    <div class="panel-heading title_channel" style="float: left;">正在播放</div>
                    <div class="panel-heading" style="float: right">
                        音量:<input type="range" id="volume" min="0" max="100" value="60"/>
                    </div>
                    <div class="panel-body">
                        <div id="box" class="right"></div>
                    </div>
                </div>
            </div>
        </div>

    </div>
</div>




    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="/static/jquery/1.8.1/jquery.js"></script>

    <%--<script src="https://cdn.bootcss.com/jquery/1.8.1/jquery.js"></script>--%>

    <script type="text/javascript">

        (function ($) {
            // 歌曲数据
            var _musicData = [
                {
                    id: 1,
                    album: '试音1',
                    author: 'Demo'
                },{
                    id: 2,
                    album: '试音2',
                    author: 'Demo'
                },{
                    id: 3,
                    album: '试音3',
                    author: 'Demo'
                },
            ];

            // 创建XMLHttp对象
            var xhr = new XMLHttpRequest();

            // 创建AudioContext对象
            var ac = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext)();

            // 创建AudioContext Gain对象
            var gainNode = ac[ac.createGain ? "createGain" : "createGainNode"]();
            // 将Gain绑定到destination
            gainNode.connect(ac.destination);

            // 创建Analyser对象
            var analyser = ac.createAnalyser();
            // 数据的长度
            var size = 128;
            // 一个无符号长整形(unsigned long)的值, 用于确定频域的 FFT (快速傅里叶变换) 的大小。
            analyser.fftSize = size * 2;
            // 将Analyser绑定到Gain
            analyser.connect(gainNode);

            // 创建Analyser(分析)对象
            //var _analyser = ac.createAnalyser();

            // 保存当前正在播放的bufferSource
            var _source = null;

            // 避免多个音频同时播放的标记
            var _playCount = 0;

            var box = $('#box')[0];
            // 定义Box窗口大小
            var height, width;

            // 创建canvas
            var canvas = document.createElement('canvas');
            var ctx = canvas.getContext("2d");
            box.appendChild(canvas);

            function resize() {
                height = box.clientHeight;
                width = box.clientWidth;
                canvas.height = height;
                canvas.width = width;
                var line = ctx.createLinearGradient(0, 0,0, height);
                line.addColorStop(0, "red");
                line.addColorStop(0.5, "yellow");
                line.addColorStop(1, "green");
                ctx.fillStyle = line;
            }

            // 当窗口改变的时候更改大小
            window.onresize = resize;

            /**
             * 加载媒体列表
             */
            var _loadAudios = function () {
                var _html = '';
                for(var _i = 0; _i < _musicData.length; _i++){
                    _html += '<li data-option="' + _musicData[_i].id + '">' + _musicData[_i].album + '- ' +  _musicData[_i].author + '</li>';
                }
                $('.ul_regions').html(_html);
            }

            /**
             * 监听点击事件
             */
            var _listenerAudiosClick = function (type) {
                $('.ul_regions>li').on('click', function() {
                    $('.title_channel').html('正在播放：' + $(this).html());
                    var id = $(this).attr('data-option');
                    var url = '/static/audioapi/data/audio'+ id+'.mp3';
                    if(type == 2){
                        _listenerAudioVolume();
                        _loadNetGain(url);
                        // 将声音初始化大小 到 60
                        changeVolume(60/100);
                    }else if(type == 3) {
                        _listenerAudioVolume();
                        _loadNetAnalyser(url);
                        changeVolume(60/100);
                    } else {// Default type == 1
                        _loadNet(url);
                    }

                });
            }

            /**
             * 载入多媒体资源文件
             */
            var _loadNet = function (url) {
                var n = ++_playCount;
                // 停止正在播放的音频
                _source && _source[_source.stop ? "stop" : "noteOff"]();
                // 中止上一次的请求
                xhr.abort();
                xhr.open("GET", url);
                xhr.responseType = "arraybuffer";
                xhr.onload = function () {
                    console.log(xhr.response);

                    if(n != _playCount)return;

                    /**
                     * 异步解码音频文件中的 ArrayBuffer;
                     * ArrayBuffer数据可以通过XMLHttpRequest和FileReader来获取.
                     * AudioBuffer是通过AudioContext采样率进行解码的,然后通过回调返回结果.
                     */
                    ac.decodeAudioData(xhr.response, function (buffer) {

                        if(n != _playCount)return;

                        /**
                         * 创建一个新的AudioBufferSourceNode接口,
                         * 该接口可以通过AudioBuffer 对象来播放音频数据.
                         * AudioBuffer对象可以通过AudioContext.createBuffer 来创建或者通过 AudioContext.decodeAudioData成功解码音轨后获取.
                         */
                        var bufferSource = ac.createBufferSource();
                        // 将解码后的buffer赋值给AudioBuffer对象
                        bufferSource.buffer = buffer;
                        // 将源连接至AudioContext.destination
                        bufferSource.connect(ac.destination);
                        // 对缓冲区中包含的音频数据的回放，或者立即开始回放。
                        bufferSource[bufferSource.start ? "start": "noteON"](0);
                        _source = bufferSource;
                    });
                }
                xhr.send();
            }

            /**
             * 载入多媒体资源文件(可以调控音量)
             */
            var _loadNetGain = function (url) {
                var n = ++_playCount;
                // 停止正在播放的音频
                _source && _source[_source.stop ? "stop" : "noteOff"]();
                // 中止上一次的请求
                xhr.abort();
                xhr.open("GET", url);
                xhr.responseType = "arraybuffer";
                xhr.onload = function () {
                    console.log(xhr.response);

                    if(n != _playCount)return;

                    /**
                     * 异步解码音频文件中的 ArrayBuffer;
                     * ArrayBuffer数据可以通过XMLHttpRequest和FileReader来获取.
                     * AudioBuffer是通过AudioContext采样率进行解码的,然后通过回调返回结果.
                     */
                    ac.decodeAudioData(xhr.response, function (buffer) {

                        if(n != _playCount)return;

                        /**
                         * 创建一个新的AudioBufferSourceNode接口,
                         * 该接口可以通过AudioBuffer 对象来播放音频数据.
                         * AudioBuffer对象可以通过AudioContext.createBuffer 来创建或者通过 AudioContext.decodeAudioData成功解码音轨后获取.
                         */
                        var bufferSource = ac.createBufferSource();
                        // 将解码后的buffer赋值给AudioBuffer对象
                        bufferSource.buffer = buffer;
                        // 将源连接至Gain对象,由于GainNode已经绑定到了destination,所以无需重复绑定
                        bufferSource.connect(gainNode);
                        // 对缓冲区中包含的音频数据的回放，或者立即开始回放。
                        bufferSource[bufferSource.start ? "start": "noteOn"](0);
                        _source = bufferSource;
                    });
                }
                xhr.send();
            }

            /**
             * 改变Gain（音量）大小
             */
            function changeVolume(percent) {
                if(gainNode && gainNode.gain){
                    gainNode.gain.value = percent * percent;
                }
            }

            /**
             * 绑定音量控件监听事件
             */
            var _listenerAudioVolume = function () {
                $('#volume').on('change', function () {
                    changeVolume(this.value / this.max);
                });
            }

            /**
             * 载入多媒体资源文件(可以调控音量、图形分析)
             */
            var _loadNetAnalyser = function (url) {
                var n = ++_playCount;
                // 停止正在播放的音频
                _source && _source[_source.stop ? "stop" : "noteOff"]();
                // 中止上一次的请求
                xhr.abort();
                xhr.open("GET", url);
                xhr.responseType = "arraybuffer";
                xhr.onload = function () {
                    console.log(xhr.response);

                    if(n != _playCount)return;

                    /**
                     * 异步解码音频文件中的 ArrayBuffer;
                     * ArrayBuffer数据可以通过XMLHttpRequest和FileReader来获取.
                     * AudioBuffer是通过AudioContext采样率进行解码的,然后通过回调返回结果.
                     */
                    ac.decodeAudioData(xhr.response, function (buffer) {

                        if(n != _playCount)return;

                        /**
                         * 创建一个新的AudioBufferSourceNode接口,
                         * 该接口可以通过AudioBuffer 对象来播放音频数据.
                         * AudioBuffer对象可以通过AudioContext.createBuffer 来创建或者通过 AudioContext.decodeAudioData成功解码音轨后获取.
                         */
                        var bufferSource = ac.createBufferSource();
                        // 将解码后的buffer赋值给AudioBuffer对象
                        bufferSource.buffer = buffer;
                        // 将bufferSource绑定到Analyser
                        bufferSource.connect(analyser);

                        // 对缓冲区中包含的音频数据的回放，或者立即开始回放。
                        bufferSource[bufferSource.start ? "start": "noteOn"](0);
                        _source = bufferSource;

                    }, function (err) {
                        console.log(err);
                    });
                }
                xhr.send();
            }

            function visualizer() {
                var arr = new Uint8Array(analyser.frequencyBinCount);
                /**
                 * 用 requestAnimationFrame 和 <canvas> 去反复收集当前音频的时域数据, 并绘制为一个示波器风格的输出(频谱).
                 * https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame
                 */
                requestAnimationFrame = window.requestAnimationFrame
                                            || window.webkitRequestAnimationFrame
                                            || window.mozRequestAnimationFrame;
                function v() {
                    analyser.getByteFrequencyData(arr);
                    //console.log(arr);
                    draw(arr);
                    requestAnimationFrame(v);
                }
                requestAnimationFrame(v);
            }


            function draw(arr) {
                ctx.clearRect(0, 0, width, height);
                var w = width / size;
                for (var i = 0;i < size; i++) {
                    var h = arr[i] / 256 * height;
                    ctx.fillRect(w * i, height - h, w * 0.6, h);
                }
            }






            function _init() {
                _loadAudios();
                _listenerAudiosClick(3);
                visualizer();
                resize();
            }

            $(function () {
                _init();
            });

        })(jQuery);



    </script>
</body>
</html>
